using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using EnvDTE80;
using Microsoft.VisualStudio.TeamFoundation.VersionControl;
using EnvDTE;
using Microsoft.VisualStudio.TeamFoundation;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Text;

namespace TeamReview.Services
{
    public static class TFSService
    {
        private static TeamFoundationServer tfs;
        private static readonly SortedList<String, List<string>> users = new SortedList<String, List<string>>();
        private static readonly object userLock = new Object();
        private static TeamProject teamProject;
        private static WorkItemStore workItemStore;
        private static FieldDefinitionCollection fields;
        private static DTE2 shellDTE;
        private static SolutionEvents solutionEvents;
        internal static event TeamProjectChangeEventHandler TeamProjectChanged;
        private static VersionControlServer versionControlServer;
        private static TeamFoundationServerExt tfsExt;
        private static VersionControlExt versionControl;

        private static void OnTeamProjectChanged()
        {

            if (teamProject == null)
                Instrumentation.Trace("TeamReview is not available because the current solution is not part of a Team Project.");
            else
                Instrumentation.Trace(String.Format(@"Team Project changed to {0}\{1}",
                                          teamProject.TeamFoundationServer.Name, teamProject.Name));

            if (TeamProjectChanged == null)
                return;

            TeamProjectChanged();
        }


        internal static VersionControlServer VersionControlServer
        {
            get { return versionControlServer; }
        }

        internal static TeamProject TeamProject
        {
            get { return teamProject; }
        }

        internal static VersionControlExt VersionControlExt
        {
            get { return versionControl; }
        }

        internal static TeamFoundationServerExt TeamFoundationServerExt
        {
            get { return tfsExt; }
        }

        internal static TeamFoundationServer TeamFoundationServer
        {
            get { return tfs; }
        }

        internal static WorkItemStore WorkItemStore
        {
            get { return workItemStore; }
        }



        internal static string CurrentUserDisplayName
        {
            get
            {
                if (workItemStore.UserDisplayMode == UserDisplayMode.AccountName)
                    return workItemStore.TeamFoundationServer.AuthenticatedUserName;

                return workItemStore.TeamFoundationServer.AuthenticatedUserDisplayName;
            }
        }

        internal static bool IsTFSProject
        {
            get
            {
                return !(teamProject == null);
            }
        }

        public static void Configure(DTE2 dte)
        {
            shellDTE = dte;
            solutionEvents = shellDTE.Events.SolutionEvents;
            solutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(SetTeamProject);
            SetTeamProject();
        }

        public static void Shutdown()
        {
            try
            {
                solutionEvents.Opened -= new _dispSolutionEvents_OpenedEventHandler(SetTeamProject);
            }
            catch { }
        }


        internal static bool HasField(string fieldName)
        {
            if (fields == null)
                return false;

            return fields.Contains(fieldName);
        }


        static void SolutionEvents_Opened()
        {
            SetTeamProject();
        }


        internal static BindingList<BindingItemQuery> GetAvailableTeamQueries()
        {
            StoredQueryCollection sq = workItemStore.Projects[teamProject.Name].StoredQueries;
            BindingList<BindingItemQuery> bl = new BindingList<BindingItemQuery>();

            foreach (StoredQuery q in sq)
            {
                if (q.QueryText.IndexOf("from WorkItemLinks") < 0 || shellDTE.Version.StartsWith("10."))
                    bl.Add(new BindingItemQuery(String.Format("{0} - {1}", q.QueryScope.ToString("G"), q.Name), q.QueryGuid));
            }
            return bl;
        }



        internal static void SetTeamProject()
        {

            TeamProject newProject;

            if (shellDTE.Solution == null)
                newProject = null;
            else
            {
                string solution = "not open";
                if (!String.IsNullOrEmpty(shellDTE.Solution.FullName))
                    solution = shellDTE.Solution.FullName;

                Instrumentation.Trace(String.Format(@"Visual Studio Solution is {0}", solution));


                versionControl = shellDTE.GetObject("Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExt") as VersionControlExt;
                if (versionControl == null || versionControl.SolutionWorkspace == null)
                    newProject = null;
                else
                    newProject = versionControl.SolutionWorkspace.GetTeamProjectForLocalPath(shellDTE.Solution.FullName);
            }

            if (teamProject != newProject)
            {
                teamProject = newProject;

                if (teamProject == null)
                {
                    OnTeamProjectChanged();
                    return;
                }


                tfs = teamProject.TeamFoundationServer;
                tfsExt = (TeamFoundationServerExt)shellDTE.GetObject("Microsoft.VisualStudio.TeamFoundation.TeamFoundationServerExt");
                versionControlServer = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));

                workItemStore = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));
                if (workItemStore == null)
                    throw new NullReferenceException("Did not retrieve WorkItemStore from Team Foundation Server");

                WorkItemType wit = GetWorkItemType();


                if (wit == null)
                    fields = null;
                else
                    fields = wit.FieldDefinitions;

                OnTeamProjectChanged();


                System.Threading.Thread thread = new System.Threading.Thread(new ThreadStart(SetUsers));
                thread.Start();
                OnTeamProjectChanged();
            }
        }


        public static WorkItemType GetWorkItemType()
        {
            if (workItemStore.Projects[teamProject.Name].WorkItemTypes.Contains(Constants.CODE_ITEM_TYPE))
                return workItemStore.Projects[teamProject.Name].WorkItemTypes[Constants.CODE_ITEM_TYPE];
            else if (workItemStore.Projects[teamProject.Name].WorkItemTypes.Contains(Constants.CODE_REVIEW_ITEM_TYPE))
                return workItemStore.Projects[teamProject.Name].WorkItemTypes[Constants.CODE_REVIEW_ITEM_TYPE];
            else
                return null;
        }

        internal static BindingList<BindingItemCodeReview> GetAvailableQueryWorkItems(Guid queryGuid)
        {
            StoredQuery scQuery = TFSService.WorkItemStore.GetStoredQuery(queryGuid);

            string project = String.Format("'{0}'", TFSService.TeamProject.Name);
            string me = String.Format("'{0}'", TFSService.TeamProject.TeamFoundationServer.AuthenticatedUserDisplayName);
            string text = scQuery.QueryText.Replace("@project", project).Replace("@me", me);

            /*
            if (text.IndexOf("from WorkItemLinks") > 0 && shellDTE.Version.StartsWith("10."))
            {
                Query linkQuery = new Query(TFSService.WorkItemStore, text);
                WorkItemLinkInfo[] links = linkQuery.RunLinkQuery();
                StringBuilder ids = new StringBuilder();
                if (links.Length == 0)
                    return new BindingList<BindingItemCodeReview>();

                for (int i = links.Length - 1; i > 0; i--)
                {
                    ids.Append(links[i].TargetId.ToString());
                    ids.Append(",");
                }
                ids.Append(links[0].TargetId.ToString());

                text = String.Format("Select [System.Id], [System.Title] From WorkItems where [System.Id] in ({0})", ids.ToString());
            }
            */

            Query query = new Query(TFSService.WorkItemStore, text);
            query.SortFieldList.Clear();
            query.SortFieldList.Add("System.Id", SortType.Ascending);

            WorkItemCollection items = TFSService.WorkItemStore.Query(query.QueryString);

            BindingList<BindingItemCodeReview> bl = new BindingList<BindingItemCodeReview>();
            for (int i = 0; i < items.Count; i++)
                bl.Add(new BindingItemCodeReview(items[i].Title, items[i].Id));

            return bl;
        }

        internal static TeamProject GetTeamProject()
        {
            return teamProject;
        }

        private static void SetUsers()
        {
            SetUsers(teamProject.ArtifactUri);

        }

        private static void SetUsers(Uri projectUri)
        {
            if (tfs == null)
                return;

            string uriString = projectUri.ToString();
            if (users.ContainsKey(uriString))
            {
                lock (userLock)
                {
                    if (users.ContainsKey(uriString))
                        return;
                }
            }

            lock (userLock)
            {
                users.Add(uriString, new List<string>());
                users[uriString].AddRange(GetCodeChangers(projectUri));
            }
        }

        internal static List<String> GetCodeChangers(Uri projectUri)
        {
            List<String> codeChangers = new List<String>();
            IGroupSecurityService gss = (IGroupSecurityService)tfs.GetService(typeof(IGroupSecurityService));

            Identity[] projectGroups = gss.ListApplicationGroups(projectUri.ToString());
            Identity serverAdminGroup = gss.ReadIdentity(SearchFactor.AccountName, "[TEAM FOUNDATION]\\Team Foundation Administrators", QueryMembership.Expanded);
            //TODO: add project collection admins
            Identity[] adminGroups = new Identity[1] { serverAdminGroup };
            Identity[] groups = new Identity[projectGroups.Length + 1];
            adminGroups.CopyTo(groups, 0);
            projectGroups.CopyTo(groups, 1);

            SortedDictionary<MemberReader, System.Threading.Thread> threads = new SortedDictionary<MemberReader, System.Threading.Thread>();
            foreach (Identity group in projectGroups)
            {
                if (!(group.DisplayName.Equals("Contributors") ||
                      group.DisplayName.Equals("Project Administrators") ||
                      group.AccountName.Equals("[TEAM FOUNDATION]\\Team Foundation Administrators")))
                    continue;
                MemberReader memberReader = new MemberReader(group, gss);

                threads.Add(memberReader, new System.Threading.Thread(new ThreadStart(memberReader.Read)));
                threads[memberReader].Start();
            }

            foreach (MemberReader reader in threads.Keys)
            {
                threads[reader].Join();
                foreach (string member in reader.MemberNames)
                {
                    if (!codeChangers.Contains(member))
                        codeChangers.Add(member);
                }
            }

            codeChangers.Sort();
            return codeChangers;
        }

        internal static List<string> GetTeamUsers(Microsoft.TeamFoundation.WorkItemTracking.Client.Project project)
        {


            SetUsers(project.Uri);
            lock (userLock)
                return users[project.Uri.ToString()];


        }

        internal static WorkItemTypeCollection GetAvailableTeamWorkItemTypes
        {
            get { return workItemStore.Projects[teamProject.Name].WorkItemTypes; }
        }

        internal static Microsoft.TeamFoundation.WorkItemTracking.Client.Project GetQueryProject(Guid queryGuid)
        {
            StoredQuery query = workItemStore.Projects[teamProject.Name].StoredQueries[queryGuid];
            if (!query.QueryText.Contains("[System.TeamProject] = @project"))
            {

                string firstFind = "[System.TeamProject] = '";
                int firstPos = query.QueryText.IndexOf(firstFind);
                int secondPos = query.QueryText.IndexOf("'", firstPos + firstFind.Length + 1);
                string project = query.QueryText.Substring((firstPos + firstFind.Length), (secondPos - firstPos - firstFind.Length));

                return workItemStore.Projects[project];
            }
            else
            {
                return query.Project;
            }

        }


    }
}
